#include "application.h"


#include <signal.h>
#include <unistd.h>

namespace johnsonli
{

    // struct A
    // {
    //     A()
    //     {
    //         auto path = johnsonli::EnvMgr::GetInstance()->getConfigPath();
    //         johnsonli::Config::LoadFromConfDir(path);
    //     }    
    // };

    // static A a;

    static johnsonli::Logger::ptr g_logger = LOG_NAME("system");

    static johnsonli::ConfigVar<std::string>::ptr g_server_work_path =
            johnsonli::Config::Lookup("server.work_path"
                    ,std::string("/apps/work/johnsonli")
                    , "server work path");

    static johnsonli::ConfigVar<std::string>::ptr g_server_pid_file =
            johnsonli::Config::Lookup("server.pid_file"
                    ,std::string("johnsonli.pid")
                    , "server pid file");

    static johnsonli::ConfigVar<std::vector<TcpServerConf> >::ptr g_servers_conf = 
            johnsonli::Config::Lookup("servers", std::vector<TcpServerConf>(), "http server config");


    Application* Application::s_instance = nullptr;

    Application::Application() {
        s_instance = this;
    }

    bool Application::getServer(const std::string& type, std::vector<TcpServer::ptr>& svrs) {
        auto it = m_servers.find(type);
        
        if(it == m_servers.end()) {
            return false;
        }

        svrs = it->second;
        
        return true;
    }


    bool Application::init(int argc, char** argv) 
    {
        m_argc = argc;
        m_argv = argv;

        johnsonli::EnvMgr::GetInstance()->addHelp("s", "start with the terminal");
        johnsonli::EnvMgr::GetInstance()->addHelp("d", "run as daemon");
        johnsonli::EnvMgr::GetInstance()->addHelp("c", "conf path default: ./conf");
        johnsonli::EnvMgr::GetInstance()->addHelp("p", "print help");

        bool is_print_help = false;

        // 环境初始化失败
        if(!johnsonli::EnvMgr::GetInstance()->init(argc, argv)) {
            is_print_help = true;
        }

        if(johnsonli::EnvMgr::GetInstance()->has("p")) {
            is_print_help = true;
        }

        // 加载配置文件
        std::string conf_path = johnsonli::EnvMgr::GetInstance()->getConfigPath();
        LOG_INFO(g_logger) << "load conf path:" << conf_path;
        johnsonli::Config::LoadFromConfDir(conf_path);

        //LOG_INFO(g_logger) << g_logger->toYamlString();


        if(is_print_help) {
            johnsonli::EnvMgr::GetInstance()->printHelp();
            return false;
        }

        int run_type = 0;
        if(johnsonli::EnvMgr::GetInstance()->has("s")) {
            run_type = 1;
        }
        if(johnsonli::EnvMgr::GetInstance()->has("d")) {
            run_type = 2;
        }

        if(run_type == 0) {
            johnsonli::EnvMgr::GetInstance()->printHelp();
            return false;
        }

        // 如果已经有进程文件，说明服务器在运行
        std::string pidfile = g_server_work_path->getValue()
                                + "/" + g_server_pid_file->getValue();
        if(johnsonli::FSUtil::IsRunningPidfile(pidfile)) {
            LOG_ERROR(g_logger) << "server is running:" << pidfile;
            return false;
        }

        // 创建工作目录
        if(!johnsonli::FSUtil::Mkdir(g_server_work_path->getValue())) {
            LOG_FATAL(g_logger) << "create work path [" << g_server_work_path->getValue()
                << " errno=" << errno << " errstr=" << strerror(errno);
            return false;
        }
        return true;

    }

    bool Application::run(Module::ptr module) 
    {
        bool is_daemon = johnsonli::EnvMgr::GetInstance()->has("d");
        return start_daemon(m_argc, m_argv,
                std::bind(&Application::main, this, std::placeholders::_1,
                    std::placeholders::_2, module), is_daemon);
    }

    int Application::main(int argc, char** argv, Module::ptr module) 
    {
        signal(SIGPIPE, SIG_IGN);
        LOG_INFO(LOG_NAME("system")) << "main";
        std::string conf_path = johnsonli::EnvMgr::GetInstance()->getConfigPath();
        johnsonli::Config::LoadFromConfDir(conf_path, true);
        {
            std::string pidfile = g_server_work_path->getValue()
                                    + "/" + g_server_pid_file->getValue();
            std::ofstream ofs(pidfile);
            if(!ofs) {
                LOG_ERROR(g_logger) << "open pidfile " << pidfile << " failed";
                return false;
            }
            ofs << getpid();
        }

        if(module == nullptr)
        {
            LOG_INFO(LOG_NAME("system")) << "module is null";
            return 0;
        }
//        LOG_INFO(LOG_NAME("system")) << module->getName();

        m_mainIOManager.reset(new johnsonli::IOManager(1, true, "main"));
        m_mainIOManager->schedule(std::bind(&Application::run_fiber, this, module));
        m_mainIOManager->stop();
        return 0;
    }

    void Application::run_fiber(Module::ptr module) 
    {
        
        if(!module->onLoad())
        {
            LOG_ERROR(g_logger) << "module error name=" << module->getName();
            _exit(0);
        }

        johnsonli::WorkerMgr::GetInstance()->init();

        auto http_confs = g_servers_conf->getValue();
        std::vector<TcpServer::ptr> svrs;
        for(auto& i : http_confs) 
        {
//            LOG_DEBUG(g_logger) << std::endl << LexicalCast<TcpServerConf, std::string>()(i);

            std::vector<Address::ptr> address;
            for(auto& a : i.address)
            {
                size_t pos = a.find(":");
                if(pos == std::string::npos) {
                    continue;
                }
                
                // 端口，IP
                int16_t port = atoi(a.substr(pos + 1).c_str());
                //auto addr = johnsonli::IPAddress::Create(a.substr(0, pos).c_str(), port);
                auto addr = johnsonli::IPv4Address::Create(a.substr(0, pos).c_str(), port);
                if(addr)
                {
                    address.push_back(addr);
                    continue;
                }

                //ip无效，用网关地址
                std::vector<std::pair<Address::ptr, uint32_t> > result;
                if(johnsonli::Address::GetInterfaceAddresses(result, a.substr(0, pos)))
                {
                    for(auto& x : result)
                    {
                        auto ipaddr = std::dynamic_pointer_cast<IPAddress>(x.first);
                        if(ipaddr)
                        {
                            ipaddr->setPort(port);
                            address.push_back(ipaddr);
                        }
                    }

                    continue;
                }

                // 网卡也不行，查域名
                auto aaddr = johnsonli::Address::LookupAny(a);
                if(aaddr) 
                {
                    address.push_back(aaddr);
                    continue;
                }

                LOG_ERROR(g_logger) << "invalid address: " << a;
                _exit(0);
            }
            
            // 监听
            IOManager* accept_worker = johnsonli::IOManager::GetThis(); 
            // io
            IOManager* io_worker = johnsonli::IOManager::GetThis();
            // 业务逻辑
            IOManager* process_worker = johnsonli::IOManager::GetThis();

            if(!i.accept_worker.empty()) 
            {
                accept_worker = johnsonli::WorkerMgr::GetInstance()->getAsIOManager(i.accept_worker).get();
                if(!accept_worker) {
                    LOG_ERROR(g_logger) << "accept_worker: " << i.accept_worker
                        << " not exists";
                    _exit(0);
                }
            }

            if(!i.io_worker.empty()) {
                io_worker = johnsonli::WorkerMgr::GetInstance()->getAsIOManager(i.io_worker).get();
                if(!io_worker) {
                    LOG_ERROR(g_logger) << "io_worker: " << i.io_worker
                        << " not exists";
                    _exit(0);
                }

                io_worker->addTimer(1000,[](){}, true);
            }
            

            if(!i.process_worker.empty()) {
                process_worker = johnsonli::WorkerMgr::GetInstance()->getAsIOManager(i.process_worker).get();
                if(!process_worker) {
                    LOG_ERROR(g_logger) << "process_worker: " << i.process_worker
                        << " not exists";
                    _exit(0);
                }
                process_worker->addTimer(1000, [](){}, true);
            }

            

            TcpServer::ptr server;
            if(i.type == "http") 
            {
                server.reset(new johnsonli::http::HttpServer(i.keepalive, i.name,
                                process_worker, io_worker, accept_worker));
            } 
            else if(i.type == "ws") 
            {
                server.reset(new johnsonli::http::WSServer(
                                i.name, process_worker, io_worker, accept_worker));
            }
            else 
            {
                LOG_ERROR(g_logger) << "invalid server type=" << i.type
                    << LexicalCast<TcpServerConf, std::string>()(i);
                _exit(0);
            }

            if(!i.name.empty()) 
            {
                server->setName(i.name);
            }

            std::vector<Address::ptr> fails;
            if(!server->bind(address, fails, false)) 
            {
                for(auto& x : fails) 
                {
                    LOG_ERROR(g_logger) << "bind address fail:"
                        << *x;
                }
                _exit(0);
            }

            //server->start();
            m_servers[i.type].push_back(server);
            svrs.push_back(server);
        }

        module->onServerReady();

        for(auto& server : svrs) {
            //LOG_ERROR(g_logger) << server->getName() << "is start";
            server->start();
        }

        module->onServerUp();
    }
}   